package com.wangijun.yuan.web.socket;


import cn.hutool.core.map.BiMap;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.wangijun.yuan.core.util.JSON;
import com.wangijun.yuan.web.util.YuanWebConst;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.rmi.RemoteException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 功能描述: 这个类里面的操作不可以依赖注入
 *
 * @author wlf 2024/8/8 22:32
 */
@Slf4j
@Service
@ServerEndpoint("/im-server")
public class WebSocketService {

    // 因为可能有多个客户端所以这里需要保证线程安全
    public final static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
    public final static BiMap<Long, String> userIdSessionIdMap = new BiMap<>(new ConcurrentHashMap<>());

    // 建立连接时执行的操作
    @OnOpen
    public void onOpen(Session session) {
        // 每个websocket连接对于服务端来说都是一个Session
        sessionMap.put(session.getId(), session);
        log.info("websocket is open");
    }


    /**
     * 收到客户端消息时执行的操作
     * @param text 接受到客户端的消息
     * @return 返回给客户端的消息
     */
    @OnMessage
    public String onMessage(Session session, String text) throws IOException {
        // 心跳数据
        if (text.equals("\r\n")) {
            return text;
        }
        if (!JSONUtil.isTypeJSON(text)) {
            throw new RemoteException("非JSON数据，连接失败");
        }

        record MessageVO(String tokenVal) {
        }

        MessageVO messageVO = JSON.parseObject(text, MessageVO.class);
        if (StrUtil.isBlank(messageVO.tokenVal())) {
            throw new RemoteException("获取Token信息失败");
        }
        // 根据Token信息获取用户ID
        String loginId = SpringUtil.getBean(StringRedisTemplate.class)
                .boundValueOps(YuanWebConst.loginToken + messageVO.tokenVal()).get();
        if (StrUtil.isBlank(loginId)) {
            throw new RemoteException("获取Token信息失败2");
        }
        // 到这里说明连接成功了，绑定用户ID和SessionID
        userIdSessionIdMap.put(Long.parseLong(loginId), session.getId());
        return "";
    }

    // 连接关闭时执行的操作
    @OnClose
    public void onClose(Session session) {
        sessionMap.remove(session.getId());
        Long key = userIdSessionIdMap.getKey(session.getId());
        if (key != null) {
            userIdSessionIdMap.remove(key);
        }
        log.info("websocket is close");
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        log.error("websocket error", throwable);
    }

    @Scheduled(cron = "-") //
    public static void sendMessage() throws IOException {
        for (String key : sessionMap.keySet()) { //给所有客户端发送消息
            sessionMap.get(key).getBasicRemote().sendText("beat");
        }
    }
    
    /**
     * 功能描述: 推送消息
     * 
     * @author wlf 2024/8/14 22:17
     */
    public static void sendMsg(Long userId, MsgVO msgVO) {
        String sessionId = userIdSessionIdMap.get(userId);
        if (sessionId == null) {
            log.warn("用户未绑定");
            return;
        }
        Session session = sessionMap.get(sessionId);
        if (session == null) {
            log.warn("用户未绑定");
            return;
        }
        try {
            session.getBasicRemote().sendText(JSON.toJSONString(msgVO));
        } catch (Exception e) {
            log.error("推送消息失败", e);
        }

    }
}
